/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 * */

// AmzServer - Tototoy's server model.
// Author: tonyjobmails@gmail.com (tonyhack).
//
// Define class UDPCrediblePatternSender.
//
// UDPCrediblePatternSender is a simple pattern witch achieves acknowledgement
// packet just like TCP, and this pattern can ensure the packet is ordered.
//

#include <string.h>

#include "udp_credible_pattern_sender.h"
#include "packet_define.h"

namespace amz {

UDPCrediblePatternSender::UDPCrediblePatternSender(uint32 max_serial_number,
         uint8 resend_times, uint32 timeout_msecs)
         : resend_times_(resend_times), timeout_msecs_(timeout_msecs),
          serial_number_pool_(packet::kMaxCredibleSerialNumber),
          ack_queue_(packet::kMTU_UDP) {}

UDPCrediblePatternSender::~UDPCrediblePatternSender() {}

bool UDPCrediblePatternSender::Available() const {
  bool allocated = false;
  this->serial_number_pool_.Get(allocated);
  return !allocated;
}

void UDPCrediblePatternSender::AskForSerialNumer(uint32 &serial_number, bool backward) {
  serial_number = this->serial_number_pool_.GetCurrentLocation();
  if(backward) {
    this->BackwardSerialNumer();
  }
}

void BackwardSerialNumer();

int UDPCrediblePatternSender::Packet(uint32 serial_number, const char *data, int size,
       char *packet, int max_size, const TimeTick &time_now) {
  int packet_size = this->CalculatePacketSize(size);
  if(max_size < packet_size) {
    return 0;
  }
  // Packet head.
  uint32 *head = ((uint32 *)packet);
  // Set UDP serial number.
  *head = serial_number;
  // Even loop, write into the head.
  if(this->serial_number_pool_.CheckEvenLoop()) {
    // Even loop.
    SET_CREDIBLE_UDP_EVEN(*head);
  } else {
    // Odd loop.
    SET_CREDIBLE_UDP_ODD(*head);
  }
  // Not ack.
  SET_CREDIBLE_UDP_NOT_ACK(*head);
  // Copy the part of data.
  char *data_part = packet + packet::kCredibleUDPHeadSize;
  memcpy(data_part, data, size);
  // Save to wait for ack.
  udp_credible_pattern_sender::AckQueueElement element(0, time_now);
  this->ack_queue_.Write(serial_number, packet, packet_size, element);
  return packet_size;
}

bool UDPCrediblePatternSender::Acknowledge(uint32 serial_number) {
  if(this->serial_number_pool_.RandomSet(serial_number, false) == false) {
    return false;
  }
  udp_credible_pattern_sender::DeleteCondition condition(this->resend_times_);
  this->ack_queue_.DeleteIf(&condition);
  return true;
}

bool UDPCrediblePatternSender::Resend(const TimeTick &time_now,
       ExecuteCallback<ExecuteElement> *resend) {
  // This condition will modify the timestamp.
  udp_credible_pattern_sender::ResendCondition condition(this->timeout_msecs_, time_now, true);
  this->ack_queue_.ConditionExecute(&condition, resend);
  return true;
}


}  // namespace amz

